﻿
using dotnet2cache.Abstractions;
using dotnet2cache.Abstractions.Bus;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace dotnet2cache.Core
{
    public class DotNetTwoCache : IDotNetTwoCache
    {
        private ILevel1CacheProvider _level1CacheProvider;
        private ILevel2CacheProvider _level2CacheProvider;
        private ILogger _log;
        private IMessageBus _messageBus;

        private string _serverTag;
        private const string TOPIC_NAME = "dotnet2cache";


        private static ConcurrentDictionary<string, CacheOptionEntry> Cache_Options = new ConcurrentDictionary<string, CacheOptionEntry>();

        public DotNetTwoCache(
            ILevel1CacheProvider level1CacheProvider, 
            ILevel2CacheProvider level2CacheProvider,
            IMessageBus messageBus,
            ILogger<DotNetTwoCache> log)
        {
            _level2CacheProvider = level2CacheProvider;
            _level1CacheProvider = level1CacheProvider;
            _messageBus = messageBus;
            _log = log;
            _serverTag = Guid.NewGuid().ToString("N");

            _messageBus.Subscribe(TOPIC_NAME, message => { OnMessage(message); });
        }


        private void OnMessage(Message message)
        {
            if(message.ServerId==_serverTag) //本服务器发送的消息，不做处理
            {
                return;
            }

            foreach(var key in message.Keys)
            {
                //检测缓存是否存在
                if (_level1CacheProvider.IsExist(key))
                {
                    _level1CacheProvider.Remove(key);
                }
            }
            



        }


        public T Get<T>(string key, Func<T> getDaataFuc = null)
        {

            //从1级缓存获取
            var cache = _level1CacheProvider.Get<T>(key);
            if (cache != null && cache.HaveValue)
            {
                return cache.Value;
            }

            //从2级缓存获取 
            cache = _level2CacheProvider.Get<T>(key);
            var option = Cache_Options.GetOrAdd(key, new CacheOptionEntry());
            if (cache != null)
            {

                lock (option)
                {
                    if (!_level1CacheProvider.IsExist(key))
                    {
                        _level1CacheProvider.Set<T>(key, cache, option);
                    }

                }

                return cache.Value;
            }
            //如果有设置回源地址则，则获取数据
            else if (getDaataFuc != null)
            {
                var data = getDaataFuc();
                Set(key, data, option);
                return data;
            }


            return default;





          
        }

        public object Get(string key, Func<object> getDaataFuc = null)
        {
            return  Get<object>(key, getDaataFuc);

        }

       

        public bool Remove(string key)
        {
            //过期二级缓存
            _level2CacheProvider.Remove(key);
            //删除一级缓存
            _level1CacheProvider.Remove(key);
            return true;
        }


        public  void Set<T>(string key, T value, CacheOptionEntry cacheOptionEntry)
        {
            var option = cacheOptionEntry;
            lock (option)
            {
                try
                {
                    if (_level1CacheProvider.IsExist(key))
                    {
                        return;
                    }

                    var cacheValue = new CacheValue<T>();
                    cacheValue.Value = value;

                    _level1CacheProvider.Set(key, cacheValue, option);
                    _level2CacheProvider.Set(key, cacheValue, option);
                }
                catch(Exception ex)
                {
                    _log.LogError(ex,$"缓存设置异常，key：{key}");
                    throw ;
                }
               
            }


        }

        public void Set<T>(string key, T value)
        {
            var option = Cache_Options.GetOrAdd(key, new CacheOptionEntry());

            Set(key, value, option);

        }

        public void Set<T>(string key, T value, DateTimeOffset absoluteExpiration)
        {
            Set(key, value, absoluteExpiration, absoluteExpiration);
        }


        public void Set<T>(string key, T value, DateTimeOffset level1AbsoluteExpiration, DateTimeOffset level2AbsoluteExpiration)
        {
            var option = Cache_Options.GetOrAdd(key, new CacheOptionEntry());

            option.AbsoluteExpiration = level1AbsoluteExpiration;
            option.Level2Expiration = level2AbsoluteExpiration;

            Set(key, value, option);
        }

        public void Set<T>(string key, T value, TimeSpan absoluteExpirationRelativeToNow)
        {
            Set(key, value, absoluteExpirationRelativeToNow, absoluteExpirationRelativeToNow);
        }


        public void Set<T>(string key, T value, TimeSpan level1AbsoluteExpirationRelativeToNow, TimeSpan level2AbsoluteExpirationRelativeToNow)
        {
            var option = Cache_Options.GetOrAdd(key, new CacheOptionEntry());

            option.AbsoluteExpirationRelativeToNow = level1AbsoluteExpirationRelativeToNow;
            option.AbsoluteExpiration = DateTimeOffset.UtcNow + level1AbsoluteExpirationRelativeToNow;

            option.Level2ExpirationRelativeToNow = level2AbsoluteExpirationRelativeToNow;
            option.Level2Expiration = DateTimeOffset.UtcNow + level2AbsoluteExpirationRelativeToNow;

            Set(key, value, option);
        }

    
    }
}
